//{==========================================================================================================
//! @file Stack.cpp
//! @date 2013-10-05
//! @mainpage Program for stack opportunities demonstrating
//! @author Kirill Shcherbatov <kshcherbatov@gmail.com>, 376 group, DCAM MIPT
//! @version 1.0
//}==========================================================================================================


//***********************************************************************************************************
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//***********************************************************************************************************

//***********************************************************************************************************
#define _EJC
//***********************************************************************************************************


//***********************************************************************************************************
//! @def   OUT
//! @note  -Use OUT to print your text in console. It is checking _EJC declaration.
//! @note  -Only if _EJC is defined then the program prints your text.
//! @note  --You also can define _EJC in compilation command (g++ main.cpp -D _EJC)
//-----------------------------------------------------------------------------------------------------------
#ifdef _EJC
    #define OUT         printf
#else
    #define OUT if (0)  printf
#endif
//***********************************************************************************************************


//***********************************************************************************************************
struct t_stack
{
    int* stack_data = NULL;
    unsigned int stack_size = 0, stack_max_size = 0;
    char stack_enable = 0;

    int stack_constructor(const unsigned int SIZE)
    {
        if (stack_enable) return 1;

        stack_enable = 1;
        stack_max_size = SIZE;
        stack_size = 0;

        stack_data = (int*)calloc (SIZE, sizeof (int));

        assert(stack_data != NULL);

        return 0;
    }

    int stack_destructor()
    {
        if (!stack_enable) return 0;

        stack_enable = 0;
        const int POISON = -666;

        for (register int i = 0; i < stack_max_size; i++)
            stack_data[i] = POISON;

        stack_size = 0;
        stack_max_size = 0;
        free(stack_data);
        stack_data = NULL;

        return 0;
    }

    int stack_is_ok()
    {
        if (
            (stack_data != NULL)
            &&
            (stack_size >= 0)
            &&
            (stack_size <= stack_max_size)
           ) return 1;  else return 0;
    }

    int stack_is_empty()
    {
        if (stack_size == 0) return 1;   else return 0;
    }

    int stack_is_full()
    {
        if (stack_size == stack_max_size) return 1;   else return 0;
    }

    int stack_push(const int value)
    {
        if (
            !(stack_is_ok())
            ||
            stack_is_full()
           )
           return 1;

        stack_data[stack_size] = value;
        stack_size++;

        return 0;
    }

    int stack_pop(int * value)
    {
        if (
            !(stack_is_ok())
            ||
            stack_is_empty()
           )
           return 1;

        stack_size--;
        *value = stack_data[stack_size];

        return 0;
    }

     int stack_dump()
    {
        OUT ("# STACK DUMP REPORT:\n\n"
             "# stack [ %x ] is %s", stack_data, (stack_is_ok()) ? "OK." : "BAD!");
        OUT ("\n"
             "Stack is in use %s\n", (stack_enable) ? "TRUE" : "FALSE");
        OUT ("# stack_max_size = %u\n"
             "# stack_size = %u\n\n",stack_max_size, stack_size);

        register int i = 0;

        for (i = 0; i < stack_size; i++)
            OUT ("# stack_data[%d] = %d (*)\n",i,stack_data[i]);

        while (i < stack_max_size)
            OUT ("# stack_data[%d] = %d\n",i,stack_data[i]), i++;

        OUT("\n");

        return 0;
    }
};
//***********************************************************************************************************


//***********************************************************************************************************
//! @param - number of sending arguments
//! @param argv[] - arguments, include generated by OS
//! @return 0 on success
//-----------------------------------------------------------------------------------------------------------
int main (int, char * argv[])
{
    OUT ("# %s\n"
         "# %s\n"
         "# %s %s:\n\n",
         argv[ 0 ], __FILE__, __DATE__, __TIME__ );

    #define $SEP_PATTERN "# <><><><><>><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>\n"
    OUT ($SEP_PATTERN
         "# Hi! This is for my stack opportunities demonstrating.\n"
         $SEP_PATTERN
         "\n");
    #undef $SEP_PATTERN

    OUT ("# Please, enter the hight-limit of new stack\n"
         "# >>  ");

    unsigned int hight_limit = 0;

    assert (scanf ("%u", &hight_limit) == 1);
    assert (hight_limit > 0);

    t_stack my_stack;

    my_stack.stack_constructor (hight_limit);

    OUT ("\n"
         "# Great! The new stack for %u elements successfully created.\n\n",hight_limit);

    #define MAIN_MENU_TEXT                                      \
    OUT ("# There is fiew commands:\n"                          \
         "#     Enter 1 to push new (value) into stack\n"       \
         "#     Enter 2 to pop last value from stack\n"         \
         "#     Enter 3 to check, if stack works correctly\n"   \
         "#     Enter 4 to get stack debug-info (dump)\n"       \
         "#     Enter 5 to destroy the stack\n"                 \
         "#     Enter 0 to exit\n\n"                            \
         "# >>    ");

    #define SMART_ASSERT(CONDITIONAL )                          \
        if (!(CONDITIONAL))                                     \
        {                                                       \
            OUT   ("ASSERTION FAILED %s\n %d\n %s\n",           \
                    __FILE__,__LINE__,#CONDITIONAL);            \
            OUT   ("Program will terminate now.\n"              \
                   "Stack before destruction:\n");              \
            my_stack.stack_dump();                              \
            my_stack.stack_destructor();                        \
            OUT   ("Stack after destruction:\n");               \
            my_stack.stack_dump();                              \
            OUT   ("Do exit...");                               \
            exit(1);                                            \
        }


    unsigned int mode = 0;
    while (1)
    {
        MAIN_MENU_TEXT;
        SMART_ASSERT(scanf ("%u", &mode) == 1);
        OUT ("\n");
        switch (mode)
        {
            case 1:
            {
                OUT ("# Pushing value...\n");
                int value_to_push = 0;
                SMART_ASSERT(scanf ("%d", &value_to_push) == 1);
                SMART_ASSERT(my_stack.stack_push (value_to_push) == 0);
                OUT ("# || Value %d successfully pushed ||\n\n", value_to_push);
                break;
            }
            case 2:
            {
                OUT ("# Getting value...\n");
                int temp = 0;
                SMART_ASSERT(my_stack.stack_pop (&temp) == 0);
                OUT ("# || Got value %d ||\n\n", temp);
                break;
            }
            case 3:
            {
                OUT ("# Getting stack stat..\n");
                OUT (my_stack.stack_is_ok() ? "# All are ok.\n\n" : "FUCKING SHIT!\n\n");
                //we don't have to destroy everythink here
                break;
            }
            case 4:
            {
                OUT ("# Getting debug information...\n");
                SMART_ASSERT(my_stack.stack_dump () == 0); // hmmm).. I do hope it could help find this line on error..
                OUT ("# || The debug information is displayed. ||\n\n");
                break;
            }
            case 5:
            {
                OUT ("# Stack destroying...\n");
                SMART_ASSERT(my_stack.stack_destructor () == 0);
                OUT ("# || Stack successfully destroyed. ||\n\n");
                break;
            }
            case 0:
            {
                OUT ("# Program exiting..\n");
                SMART_ASSERT(my_stack.stack_destructor () == 0);
                OUT ("# || Stack successfully destroyed. ||\n");
                OUT ("# Do exit.\n\n");
                exit(0);
                break;
            }
            default: OUT ("Unknown command\n");
        }
    }
    #undef SMART_ASSERT
    #define MAIN_MENU_TEXT
    return 0;
}
//***********************************************************************************************************
